// $Id: array.c 142 2013-06-15 05:19:57Z alan.watson.f $

#include <stdlib.h>

#include "oaxaca-config.h"
#include "oaxaca.h"
#include "array.h"

array_short_t *
alloc_array_short(size_t ny, size_t nx)
{
  array_short_t *a = malloc(sizeof(*a));
  if (a == NULL)
    oaxaca_error("memory allocation failed.");

  a->nx = nx;
  a->ny = ny;
  a->alloced = 1;
  a->z = malloc(ny * sizeof(*a->z));
  if (a->z == NULL) {
    free(a);
    oaxaca_error("memory allocation failed.");
  }

  a->z[0] = malloc(nx * ny * sizeof(*a->z[0]));
  if (a->z[0] == NULL) {
    free(a->z);
    free(a);
    oaxaca_error("memory allocation failed.");
  }

  for (size_t iy = 1; iy < ny; ++iy)
    a->z[iy] = a->z[0] + iy * nx;
    
  return a;
}

array_long_t *
alloc_array_long(size_t ny, size_t nx)
{
  array_long_t *a = malloc(sizeof(*a));
  if (a == NULL)
    oaxaca_error("memory allocation failed.");

  a->nx = nx;
  a->ny = ny;
  a->alloced = 1;
  a->z = malloc(ny * sizeof(*a->z));
  if (a->z == NULL) {
    free(a);
    oaxaca_error("memory allocation failed.");
  }

  a->z[0] = malloc(nx * ny * sizeof(*a->z[0]));
  if (a->z[0] == NULL) {
    free(a->z);
    free(a);
    oaxaca_error("memory allocation failed.");
  }

  for (size_t iy = 1; iy < ny; ++iy)
    a->z[iy] = a->z[0] + iy * nx;
    
  return a;
}

array_double_t *
alloc_array_double(size_t ny, size_t nx)
{
  array_double_t *a = malloc(sizeof(*a));
  if (a == NULL)
    oaxaca_error("memory allocation failed.");

  a->nx = nx;
  a->ny = ny;
  a->alloced = 1;
  a->z = malloc(ny * sizeof(*a->z));
  if (a->z == NULL) {
    free(a);
    oaxaca_error("memory allocation failed.");
  }

  a->z[0] = malloc(nx * ny * sizeof(*a->z[0]));
  if (a->z[0] == NULL) {
    free(a->z);
    free(a);
    oaxaca_error("memory allocation failed.");
  }

  for (size_t iy = 1; iy < ny; ++iy)
    a->z[iy] = a->z[0] + iy * nx;
    
  return a;
}

array_short_t *
wrap_array_short(size_t ny, size_t nx, short *z, size_t stride)
{
  array_short_t *a = malloc(sizeof(*a));
  if (a == NULL)
    oaxaca_error("memory allocation failed.");

  a->nx = nx;
  a->ny = ny;
  a->alloced = 0;
  a->z = malloc(ny * sizeof(*a->z));
  if (a->z == NULL) {
    free(a);
    oaxaca_error("memory allocation failed.");
  }

  for (size_t iy = 0; iy < ny; ++iy)
    a->z[iy] = z + iy * stride;
    
  return a;
}

array_long_t *
wrap_array_long(size_t ny, size_t nx, long *z, size_t stride)
{
  array_long_t *a = malloc(sizeof(*a));
  if (a == NULL)
    oaxaca_error("memory allocation failed.");

  a->nx = nx;
  a->ny = ny;
  a->alloced = 0;
  a->z = malloc(ny * sizeof(*a->z));
  if (a->z == NULL) {
    free(a);
    oaxaca_error("memory allocation failed.");
  }

  for (size_t iy = 0; iy < ny; ++iy)
    a->z[iy] = z + iy * stride;
    
  return a;
}

array_double_t *
wrap_array_double(size_t ny, size_t nx, double *z, size_t stride)
{
  array_double_t *a = malloc(sizeof(*a));
  if (a == NULL)
    oaxaca_error("memory allocation failed.");

  a->nx = nx;
  a->ny = ny;
  a->alloced = 0;
  a->z = malloc(ny * sizeof(*a->z));
  if (a->z == NULL) {
    free(a);
    oaxaca_error("memory allocation failed.");
  }

  for (size_t iy = 0; iy < ny; ++iy)
    a->z[iy] = z + iy * stride;
    
  return a;
}

void
free_array_short(array_short_t *a)
{
  if (a != NULL) {
    if (a->alloced)
      free(a->z[0]);
    free(a->z);
    free(a);
  }
}

void
free_array_long(array_long_t *a)
{
  if (a != NULL) {
    if (a->alloced)
      free(a->z[0]);
    free(a->z);
    free(a);
  }
}

void
free_array_double(array_double_t *a)
{
  if (a != NULL) {
    if (a->alloced)
      free(a->z[0]);
    free(a->z);
    free(a);
  }
}
